package com.gxc.browser.config;

import com.gxc.browser.config.password.MyPasswordEncoderFactories;
import com.gxc.core.config.SecurityProperties;
import com.gxc.core.config.SmsCodeAuthenticationSecurityConfig;
import com.gxc.core.filter.SmsCodeFilter;
import com.gxc.core.filter.ValidateCodeFilter;
import com.gxc.core.properties.MatchersProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author GongXincheng
 * @since 2019-10-13 20:23
 */
@Configuration
public class BrowserSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return MyPasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Resource
    private SecurityProperties securityProperties;

    @Resource
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Resource
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Resource
    private DataSource dataSource;

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    /**
     * 记住我
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 启动的时候，自动建表，注意只能使用一次，如果表已经建成功，再次启动 报错
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        HttpSecurity httpSecurity = http
                // 添加图片验证码校验器
                .addFilterBefore(new ValidateCodeFilter(authenticationFailureHandler), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new SmsCodeFilter(authenticationFailureHandler), UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                // 配置登录页
                .loginPage("/authentication/request")
                // form表单中的action中的uri
                .loginProcessingUrl("/authentication/form")
                // 表单登录成功处理器
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
                .and();


        // 记住我
        httpSecurity.rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                .userDetailsService(userDetailsService);

        // 需要认证的请求
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
        registry.antMatchers("/error").permitAll();

        // 从配置文件读取.
        for (MatchersProperties matcher : securityProperties.getMatchers()) {
            if ("permitAll".equalsIgnoreCase(matcher.getAttribute())) {
                // 过滤掉获取登录页面的请求
                registry.antMatchers(matcher.getPath()).permitAll();
            } else if ("authenticated".equalsIgnoreCase(matcher.getAttribute())) {
                registry.antMatchers(matcher.getPath()).authenticated();
            }
        }

        // 任何请求
        registry.anyRequest()
                // 需要被认证授权
                .authenticated()
                .and()
                // 关闭csrf
                .csrf()
                .disable()
                // 自定义手机短信认证
                .apply(smsCodeAuthenticationSecurityConfig);

    }
}
